Факультет «Інформатика та системи управління»
Кафедра "Програмне забезпечення ЕОМ та інформаційні технології»
Курсовий проект
з системного програмування
Розрахунково-пояснювальна записка
Тема:
«Протоколювання обміну інформацією між комп'ютером і зовнішнім запам'ятовуючим USB пристроєм »
Зміст
Введення
1. Аналітичний розділ
1.1 Постановка завдання
1.2 Архітектура Windows NT 5
1.3 Шина USB
1.3.1 Внутрішня організація шини USB
1.4 драйверне модель WDM
1.4.1 драйверне шари
1.4.2 Точки входу WDM-драйвера
1.5 Пакет запиту вводу / виводу (IRP)
1.6 Рівні запиту переривань
1.7 Повідомлення про завершення запиту нижчестоящим драйвером
1.8 Робота з файлами в режимі ядра
1.9 Робота з реєстром в режимі ядра
1.10 MDL списки
2. Конструкторський розділ
2.1 Точки входу розроблюваного драйвера
2.1.1 Функція DriverEntry
2.1.2 Функція AddDevice
2.1.3 Функція DriverUnload
2.1.4 Функція DispatchRoutine
2.1.5 Функція DispatchInternalDeviceControl
2.2 Розміщення коду драйвера в пам'яті
2.3 Установка драйвера в системі
3. Технологічний розділ
3.1 Вибір мови і засобів програмування
3.1.1 Драйвер-фільтр
3.1.2 Управляє додаток
3.2 Структури даних драйвера-фільтра
3.2 Інтерфейс керуючого програми
3.3 Тестування драйвера-фільтра
Висновок
Список літератури та інтернет-ресурсів
Введення
При вирішенні широкого кола завдань виникає необхідність в отриманні інформації про функціонування будь-якого зовнішнього пристрою комп'ютера. До категорії цих завдань можна віднести розробку систем інформаційної безпеки, що дуже актуально в сучасному світі, де інформація є одним з найважливіших ресурсів.
Одним з компонентів системи інформаційної безпеки може бути модуль, що виконує протоколювання обміну інформацією між комп'ютером і деяким зовнішнім запам'ятовуючим пристроєм, наприклад USB накопичувачем.
1. Аналітичний розділ
1.1 Постановка завдання
Відповідно до завдання на курсову роботу необхідно розробити програмний комплекс, що забезпечує спостереження за обміном інформацією між комп'ютером і зовнішнім запам'ятовуючим USB пристроєм.
Перерахуємо вимоги, які пред'являються до програмного комплексу:
Розроблюваний комплекс повинен відстежувати запити на читання і запис, що приходять до пристрою;
Від програми не потрібно визначати, до яких файлів вироблялося звернення, а лише те, які дані зчитувалися і записувалися;
Збережена інформація повинна володіти структурованістю, таким чином, що при аналізі лог-файл, можна було визначити, передавалася або приймалася інформація і в якій кількості;
Програмний комплекс не повинен призводити до збоїв у роботі операційної системи;
Програма установки повинна коректно обробляти спробу встановлення на один пристрій у двох примірниках.
1.2 Архітектура Windows NT 5
Архітектура Windows NT 5 відповідає класичним уявленням про проектування операційних систем. Найбільш поширені реалізації даної ОС для платформи Intel x 86 у одно-або багатопроцесорних конфігураціях, проте існують також версії для DEC Alpha і MIPS. Дана операційна система використовує захищений режим центрального процесора, реалізує механізми віртуальної пам'яті і багатозадачності.
Виконуваний код у Windows NT 5 має два рівні привілеїв: код режиму користувача і код режиму ядра. Рівень привілеїв накладає певні обмеження: у режимі користувача не можуть виконуватися привілейовані інструкції процесора, що не дозволено звернення до захищених сторінок пам'яті. Ці обмеження накладаються для забезпечення безпеки роботи системи. Для користувача додаток не повинно мати можливість в результаті помилки або навмисно вносити зміни в критичні таблиці операційної системи або в пам'ять інших додатків. Зокрема, такі обмеження забороняють користувача додатком безпосередньо керувати зовнішніми пристроями, тому що кожне з них є ресурсом, що розділяється.
У Windows NT 5 забезпечення обміну даними і управління доступом до зовнішнього пристрою як до ресурсу покладається на його драйвер. Введення і виведення в драйверах здійснюється за допомогою IRP пакетів (Input / Output Request Packet). Запити на введення / висновок, що їх посилають додатками або іншими драйверами, обробляються драйвером, після чого запитуючої програмою в тому ж пакеті надсилається статус завершення операції. Детальніше про пакети вводу / виводу буде сказано далі, загальний же принцип взаємодії проілюстровано на Рис. 1.2.1.
Рис. 1.2.2 Архітектура введення / виводу Windows NT 5
Стосовно до поставленого завдання, з вищевикладеного випливає, що повне протоколювання обміну даними із зовнішнім пристроєм може бути здійснене тільки на рівні драйвера.
Управління зовнішнім пристроєм в загальному випадку зводиться до заповнення його регістрів необхідними даними. Монопольний доступ драйвера до цих регістрів гарантується операційною системою. Очевидно, що за даних обставин потрібно, щоб драйвер пристрою виконувався в режимі ядра.
Узагальнена класифікація драйверів Windows NT 5 може бути представлена наступним чином:
Драйвери режиму ядра:
Успадковані драйвери;
Драйвери файлової системи;
Відеодрайвери;
Драйвери PnP (Plug And Play):
Драйвери WDM.
Драйвери користувальницького режиму.
1.3 Шина USB
Специфікація USB була розроблена консорціумом компаній, включаючи Intel та Microsoft. Метою нового стандарту було забезпечення організації недорогий средньошвидкісною шини в таких областях застосування, як передача цифрових зображень, комп'ютерна телефонія та мультимедійні ігри. Поточними версіями специфікації USB є версії 1.1 та 2.0 (в другу закладені більш високі швидкісні характеристики).
Гранична швидкість передачі даних по шині USB специфікації 1.1 складає 12 Мбіт / с (Full Speed). Повільні пристрої використовують низьку швидкість передачі - 1,5 Мбіт / с (Low Speed). Стандарт USB версії 2.0 підтримує фізичну швидкість передачі до 480 Мбіт / с (High Speed). Дані передаються послідовно по парі провідників. Харчування для деяких пристроїв доступно за окремим провідникам харчування та заземлення (для пристроїв з невеликим енергоспоживанням).
Пристрої USB можуть бути підключені 5 - метровим кабелем (а практично - і більш довгим). Використання USB хаба (hub - концентратор) дозволяє збільшити дальність розміщення пристроїв від хост-контролера, а так само кількість пристроїв, що підключаються до однієї шині USB. Послідовно можна підключити до п'яти хабів, забезпечивши довжину з'єднання 30 метрів. До хост-контролера можна підключити до 127 пристроїв, шинний адресу яких встановлюється динамічно при підключенні пристроїв.
На малюнку 1.3.1 наведений приклад конфігурації мережі USB пристроїв:
Рис. 1.3.1 Мережа USB пристроїв
Робота програміста, що створює драйвер зовнішнього (не знаходиться на материнській платі) USB пристрою зводиться до того, щоб скористатися програмним інтерфейсом системних драйверів шини USB, спілкування з яким відбувається за допомогою пакетів, званих URB (USB Request Block) пакетами. Робота з регістрами USB контролерів на материнській платі тепер стала долею вузького кола фахівців - розробників материнських плат та операційних систем. Всім іншим розробникам USB пристроїв в операційній системі Windows пропонується достатньо розвинений програмний інтерфейс WDM драйверів, які беруть на себе всі апаратно-орієнтовані операції.
1.3.1 Внутрішня організація шини USB
Всі операції з передачі даних по шині USB ініціюються хостом. Периферійні пристрої не можуть самі почати обмін даними, вони можуть тільки реагувати на команди хоста. Розглянемо загальну схему обміну даними по шині USB.
Система USB поділяється на три логічних рівня з певними правилами взаємодії. Пристрій USB містить інтерфейсну, логічну і функціональну частини. Хост теж ділиться на три частини: інтерфейсну, системну та програмне забезпечення. Кожна частина відповідає тільки за певне коло завдань. Логічне і реальна взаємодія між ними показано на малюнку 1.3.1.1.
Рис. 1.3.1.1 Взаємодія компонентів USB
Таким чином, операція обміну даними між прикладною програмою і шиною USB виконується шляхом передачі буферів пам'яті через такі рівні:
рівень клієнтського програмного забезпечення в хості - зазвичай представляється драйвером пристрою USB, забезпечує взаємодію користувача з операційною системою з одного боку і системним драйвером з іншого;
рівень системного програмного забезпечення USB в хості (USBD, Universal Serial Bus Driver) - керує нумерацією пристроїв на шині, управляє розподілом пропускної здатності шини та потужності харчування, обробляє запити користувача драйверів;
хост-контролер інтерфейсу шини USB (HCD, Host Controller Driver) - перетворює запити вводу / виводу в структури даних, за якими хост-контролер виконує фізичні транзакції, працює з регістрами хост-контролера.
Рівень клієнтського програмного забезпечення визначає тип передачі даних, необхідний для виконання затребуваної прикладної програмою операції. Після визначення типу передачі даних цей рівень передає системного рівня наступне:
буфер пам'яті, званий клієнтським буфером;
пакет IRP, який вказує тип необхідної операції. Безпосередньою обробкою запиту займається системний драйвер USB.
Рівень системного драйвера USB необхідний для управління ресурсами USB. Він відповідає за виконання наступних дій:
розподіл смуги пропускання шини USB;
призначення логічних адрес пристроїв кожній фізичній USB пристрою;
планування транзакцій.
Логічне пристрій USB являє собою набір незалежних кінцевих точок, з якими клієнтське програмне забезпечення обмінюється інформацією. Кожному логічного пристрою USB призначається свою адресу, унікальний на даній шині USB. Кожна кінцева точка логічного пристрою ідентифікується своїм номером і напрямом передачі (IN - передача до хоста, OUT - від хоста).
Транзакція на шині USB - це послідовність обміну пакетами між хостом і периферійним пристроєм, в ході якої може бути переданий або прийнятий один пакет даних. Коли клієнтське програмне забезпечення передає IRP рівню системного драйвера, USB драйвер перетворює їх в одну або кілька транзакцій шини і потім передає вийшов перелік транзакцій драйверу контролера хоста.
Системний драйвер USB складається з драйвера USB і драйвера хост-контролера. Коли клієнтський рівень передає IRP рівню системного забезпечення USB, USB драйвер перетворює їх в одну або кілька транзакцій шини і потім передає вийшов перелік транзакцій драйверу контролера хоста. Драйвер контролера хоста приймає від системного драйвера шини перелік транзакцій і виконує наступні дії:
планує виконання отриманих транзакцій, додаючи їх до списку транзакцій;
витягує зі списку чергову транзакцію, і передає її рівню хост-контролера інтерфейсу шини USB;
відстежує стан кожної транзакції аж до її завершення.
При виконанні всіх пов'язаних з командним пакетом транзакцій системний рівень повідомляє про це клієнтський рівень.
Рівень хост-контролера інтерфейсу шини USB отримує окремі транзакції від драйвера контролера хоста (у складі рівня системного забезпечення USB) і перетворює їх у відповідну послідовність операцій шини. У результаті цього USB пакети передаються вздовж усієї фізичної ієрархії хабів до периферійного USB пристрою.
Нижній рівень периферійного USB пристрою називається рівнем інтерфейсу шини USB. Він взаємодіє з інтерфейсним рівнем шини USB на стороні хоста і передає пакети даних від хоста до периферійного пристрою у форматі, визначеному специфікацією USB. Потім він передає пакети вгору - рівню логічного USB пристрою.
Середній рівень периферійного пристрою USB пристрою називається рівнем логічного USB пристрою. Кожне логічне USB пристрій представляється набором своїх кінцевих точок, з якими може взаємодіяти системний рівень USB хоста. Ці точки є джерелами і приймачами всіх комунікаційних потоків між хостом і периферійними пристроями USB.
Самий верхній рівень периферійного USB пристрою називається функціональним рівнем. Цей рівень відповідає рівню клієнтського забезпечення хоста. З точки зору клієнтського рівня, нижчележащі рівні потрібні для організації між ним і кінцевими точками прямих «каналів даних», які йдуть аж до функціонального рівня. А з точки зору нашої схеми функціональний рівень виконує наступні дії:
отримує дані, що їх посилають клієнтським рівнем хоста з кінцевих точок каналів даних нижчого рівня логічного USB пристрої;
посилає дані клієнтського рівня хоста, направляючи їх в кінцеві точки каналів даних нижчого рівня логічного USB пристрою.
Логічно передача даних між кінцевою точкою і програмним забезпеченням проводиться за допомогою виділення каналу і обміну даними по цьому каналу, а з точки зору представлених рівнів, передача даних виглядає наступним чином:
Рис. 1.3.1.2 Рівні передачі даних
Кінцева точка (Endpoint) - це частина USB пристрою, яка має унікальний ідентифікатор і є одержувачем або відправником інформації, що передається по шині USB. Простіше кажучи, це буфер, який зберігає кілька байт. Зазвичай це блок даних у пам'яті чи регістр мікроконтролера. Дані, що зберігаються в кінцевій точці, можуть бути або прийнятими даними, або даними, які очікували передачу. Хост також має буфер для прийому і передачі даних, але хост не має кінцевих точок.
Кінцева точка має наступні основні параметри:
частота доступу до шини;
допустима величина затримки обслуговування;
необхідна ширина смуги пропускання каналу;
номер кінцевої точки;
спосіб обробки помилок;
максимальний розмір пакету, який кінцева точка може приймати або відправляти;
використовуваний кінцевою точкою тип посилок;
напрям передачі даних.
Будь-яке USB пристрій має кінцеву точку з нульовим номером (Endpoint Zero). Ця точка дозволяє хосту опитувати пристрій з метою визначення його типу і параметрів, виконувати ініціалізацію та конфігурування пристрою.
Крім нульової точки, пристрої, звичайно, мають додаткові кінцеві точки, які використовуються для обміну даними з хостом. Додаткові точки можуть працювати або тільки на прийом даних від хоста (вхідні точки, IN), або тільки на передачу даних хосту (вихідні точки, OUT).
Нульова точка пристрою доступна після того, як пристрій підключено до шини, включено і отримало сигнал скидання по шині (bus reset). Всі інші кінцеві точки після включення живлення або скидання перебувають у невизначеному стані і недоступні для роботи до тих пір, поки хост не виконає процедуру конфігурування пристрою.
Специфікація шини визначає чотири різних типу передачі даних для кінцевих точок:
керуючі передачі (Control Transfers) - використовуються хостом для конфігурування пристрою під час підключення, для керування пристроєм і отримання статусної інформації в процесі роботи. Протокол забезпечує гарантовану доставку таких посилок;
передачі масивів даних (Bulk Data Transfers) - застосовуються при необхідності забезпечення гарантованої доставки даних від хоста до функції або від функції до хоста, але час доставки не обмежена;
передачі по перериваннях (Interrupt Transfers) - використовують у тому випадку, коли потрібно передавати одиночні пакети даних невеликого розміру. Кожен пакунок потрібен передати за обмежений час. Операції передачі носять спонтанний характер і повинні обслуговуватися не повільніше, ніж того вимагає пристрій;
ізохронних передачі (Isochronous Transfers) - застосовуються для обміну даними в «реальному часі», коли на кожному часовому інтервалі потрібно передавати строго певну кількість даних, але доставка інформації не гарантована (передача даних ведеться без повторення при збоях, допускається втрата пакетів).
Канал (Pipe) - це логічне з'єднання між кінцевою точкою пристрої та ПЗ хоста. Існує дві моделі каналів:
потоковий канал (або просто потік, streaming pipe) - це канал для передачі даних, структура яких визначається клієнтським ПЗ. Потоки використовуються для передачі масивів даних, передачі даних по перериваннях і ізохронної передачі даних. Потік завжди односпрямований. Один і той же номер кінцевої точки може використовуватися для двох різних потокових каналів - введення і виведення. Передачі даних в потокових каналах підпорядковуються наступним правилам:
запити клієнтських драйверів для різних каналів, поставлені в певному порядку один відносно одного, можуть виконуватися в іншому порядку;
запити для одного каналу будуть виконуватися строго в порядку їх надходження;
якщо під час виконання будь-якого запиту відбувається серйозна помилка (STALL), потік зупиняється;
канал повідомлень (message pipe або control pipe) - це канал для передачі даних, структура яких визначається специфікацією USB. Канали цього типу двонаправлені і застосовуються для передачі керуючих посилок. Канали повідомлень суворо синхронізовані - специфікація USB забороняє одночасну обробку декількох запитів: не можна починати передачу нового повідомлення, поки не завершена обробка попереднього. У разі виникнення помилки передача повідомлення може бути перервана хостом, після чого хост може почати передачу нового повідомлення.
Основними характеристиками каналів є:
смуга пропускання каналу;
використовуваний каналом тип передачі даних;
характеристики, що відповідають кінцевій точці: напрямок передачі даних і максимальний розмір пакету.
Смуга пропускання шини ділиться між усіма встановленими каналами. Виділена смуга закріплюється за каналом, і якщо встановлення нового каналу вимагає такої смуги, яка не списується у вже існуючий розподіл, запит на виділення каналу відкидається. Архітектура USB передбачає внутрішню буферизацію всіх пристроїв, причому, чим більшою смуги пропускання вимагає пристрій, тим більше повинен бути його буфер. Шина USB повинна забезпечувати обмін з такою швидкістю, щоб затримка даних у пристрої, викликана буферизацією, не перевищувала декількох мілісекунд.
Канал повідомлень, пов'язаний з нульовою кінцевою точкою, називається Основним каналом повідомлень (Default Control Pipe або Control Pipe 0). Власником цього каналу є USBD, і він використовується для конфігурування пристрою. Основний канал повідомлень підтримує тільки керуючі передачі. Решта канали (вони називаються клієнтськими каналами, Client Pipe) створюються в процесі конфігурування пристрою. Їх власниками є драйвери пристроїв. По клієнтських каналах можуть передаватися як потоки, так і повідомлення за допомогою будь-яких типів передач.
Набір клієнтських каналів, з якими працює драйвер пристрою, називається інтерфейсом пристрою або зв'язкою клієнтських каналів.
1.4 драйверне модель WDM
WDM (Windows Driver Model) - нова модель архітектури драйверів, запропонована Microsoft для Windows 2000, хоча ця архітектура розвивалася, починаючи з Windows 3.11, продовжуючи розвиватися і в Windows 98 і Windows NT, але по-справжньому повної вона стала тільки в Windows 2000.
З точки зору WDM, існує три типи драйверів:
Драйвер шини - драйвер, обслуговуючий контролер шини, адаптер, міст або будь-які інші пристрої, що мають дочірні пристрою. Для кожного типу шини в операційній системі є свій драйвер;
Функціональний драйвер - основний драйвер пристрою, що надає його функціональний інтерфейс. Цей драйвер обов'язковий крім тих випадків, коли введення-виведення здійснюється драйвером шини або драйвером фільтрів шини. Функціональний драйвер за визначенням має найбільш повною інформацією про своє пристрої. Зазвичай тільки цей драйвер має доступ до специфічних регістрів пристрою;
Драйвер фільтра - драйвер, що підтримує додаткову функціональність пристрою (або існуючого драйвера) або змінює запити вводу / виводу і відповіді на них від інших драйверів. Таких драйверів може бути декілька, хоча їх присутність необов'язкова. Вони можуть працювати як на більш високому рівні, ніж функціональний драйвер або драйвер шини, так і на більш низькому.
У середовищі WDM один драйвер не може контролювати всі аспекти пристрої: драйвер шини інформує диспетчера PnP про пристрої, підключених до шини, в той час як функціональний драйвер управляє пристроєм.
1.4.1 драйверне шари
Згідно перерахованим вище типам драйверів, існує три типи
об'єктів:
Об'єкти фізичних пристроїв (PDO, Physical Device Object) - ці об'єкти створюються для кожного фізично ідентифікованого елемента апаратури, підключеного до шини даних;
Об'єкти функціональних пристроїв (FDO, Functional Device Object) - передбачає одиницю логічної функціональності пристрою;
Об'єкти фільтрів пристроїв (FiDO, Filter Device Object) - надають додаткову функціональність.
У Windows NT 5 послідовність завантаження драйверів пристроїв така:
Під час завантаження операційної системи проводиться завантаження шинних драйверів для кожної відомій системі шини (список шин створюється при установці операційної системи і зберігається в реєстрі);
Викликається DriverEntry, а потім AddDevice для кожного шинного драйвера. У AddDevice створюється FDO для драйвера системної шини. Потім на створений FDO відправляється запит IRP _ MN _ START _ DEVICE;
Шинний драйвер складає список всіх пристроїв, підключених до шини. Для кожного знайденого пристрою створюється об'єкт PDO;
На кожен PDO надсилається запит IRP _ MN _ QUERY _ DEVICE _ RELATION, у відповідь на який шинний драйвер повертає ідентифікатори всіх знайдених пристроїв;
На ці PDO надсилають запит IRP _ MN _ QUERY _ ID, у відповідь на який драйвер системної шини повідомляє ідентифікатори цих пристроїв;
Отримавши ідентифікатори, система намагається знайти і завантажити драйвери пристроїв;
Знайшовши драйвер для пристроїв, система завантажує його в пам'ять, викликаючи його DriverEntry. Потім викликається AddDevice, де створюється FDO для пристрою. Якщо пристроїв, керованих цим драйвером, декілька, то AddDevice буде викликана для кожного пристрою. Якщо в реєстрі зареєстровано додаткові фільтри, то вони також завантажуються в пам'ять. Потім система посилає на FDO запит IRP _ MN _ START _ DEVICE;
Відбувається посилка на FDO запиту IRP _ MN _ QUERY _ DEVICE _ RELATIONS. Якщо пристрій сам є шиною чи тримає на собі інші пристрої, якими само не керує, то для пристрою на ньому повторюється вся послідовність дій, починаючи з пункту 5.
Функція AddDevice, що викликається для кожного FDO, викликає IoCreateDevice і IoAttachDeviceToStack, забезпечуючи побудова стека пристроїв. Стек пристроїв забезпечує проходження запитів від користувача програм до апаратного (нижнього) рівня драйверів (Мал. 1.4.1.1).
Рис. 1.4.1.1 Стек пристроїв
З вищесказаного стає зрозумілим, що розробляється драйвер повинен бути драйвером-фільтром нижнього рівня, пов'язаних з клієнтським драйвером USB накопичувача. Необхідність роботи з клієнтським USB драйвером пояснюється тим, що саме на цьому рівні перехоплюється інформація має необхідної структурованістю - передаються саме файли, а не блоки інформації (кадри або складені з них транзакції), що визначаються протоколом обміну через USB.
1.4.2 Точки входу WDM - драйвера
WDM драйвери відрізняються від успадкованих драйверів тим, що повинні містити додаткові точки входу для підтримки PnP, і, в цілому, вони більш логічні по структурі. Наведемо список точок входу і коротко охарактеризуємо їх призначення:
NTSTATUS DriverEntry (
IN PDRIVER _ OBJECT DriverObject, / / покажчик на об'єкт драйвера
IN PUNICODE _ STRING RegistryPath) / / шлях до підрозділу регістра,
/ / Відноситься до драйвера
Ця функція виконується при завантаженні драйвера операційною системою. У WDM драйвери на DriverEntry покладені обов'язки по реєстрації всіх інших точок входу драйвера.
NTSTATUS AddDevice (
IN PDRIVER_OBJECT DriverObject, / / покажчик на об'єкт драйвера
IN PDEVICE _ OBJECT PhysicalDeviceObject) / / покажчик на батьківський PDO
У драйверах, що підтримують PnP, через цю точку входу менеджер PnP посилає драйверу повідомлення про виявлення пристрою, за яке має відповідати драйвер. Функція AddDevice повинна створити об'єкт пристрою за допомогою виклику IoCreateDevice і при необхідності приєднати його до стека пристроїв з допомогою IoAttachDeviceToDeviceStack.
NTSTATUS DriverUnload (
IN PDRIVER_OBJECT DriverObject) / / покажчик на об'єкт драйвера
Викликається при вивантаженні драйвера. У цієї функції повинні осво
Бождан всі витребувані драйвером ресурси. Драйвери WDM моделі
виконують ці дії в обробнику запитів IRP _ MJ _ PNP з субкод IRP _ MN _ REMOVE _ DEVICE, тобто при видаленні пристрою з системи.
Слід виділити окремий клас точок входу драйвера, які призначені для обробки IRP пакетів з різними кодами операцій. Ці точки входу реєструються при завантаженні драйвера у функції DriverEntry. Реєстрація проводиться шляхом заповнення елементів масиву MajorFunction адресами діспетчерізуемих функцій. Індексом в цьому масиві є коди IRP _ MJ _ XXX, то є описані числами типи пакетів IRP. Диспетчер введення / виведення, орієнтуючись на заповнення цього масиву, викликає потрібні функції драйвера.
Оскільки для драйвера важливі тільки адреси робочих процедур, то всі робочі процедури можуть мати абсолютно довільні імена.
1.5 Пакет запиту вводу / виводу (IRP)
Пакети вводу / виводу (IRP пакети) використовуються для передачі запитів до драйвера від його клієнтів. Вони є структурами даних змінної довжини, і складаються із стандартного заголовка, що містить загальну облікову інформацію, і одного або декількох блоків параметрів, які називаються осередками стека вводу / виводу (I / O Stack Location).
Наведемо структуру заголовка IRP пакету:
Таблиця 1.5.1. Структура заголовка IRP пакета.
Поля | Опис |
IO_STATUS_BLOCK IoStatus | Статус запиту |
PVOID AssociatedIrp. SystemBuffer | Покажчик на системний буфер для випадку, якщо пристрій підтримує буферизованная введення / висновок |
PMDL MdlAddress | Покажчик на MDL список у випадку, якщо пристрій підтримує пряме введення / висновок |
PVOID UserBuffer | Адреса для користувача буфера для введення / виводу |
BOOLEAN Cancel | Індикатор того, що IRP пакет повинен бути анульований |
Основне призначення осередків стека введення / виведення полягає в тому, щоб зберігати функціональний код і параметри запиту на ввід / вивід. Нижче, в таблиці 1.5.2 наводяться поля комірок стека вводу / виводу, до яких драйвер може звертатися безпосередньо за вказівником (чого не рекомендується робити для інших полів):
Таблиця 1.5.2. Структура комірки стека вводу / виводу
Поля | Опис |
UCHAR MajorFunction | Код IRP _ MJ _ XXX, що описує призначення операції |
UCHAR MinorFunction | Субкод операції |
PDEVICE_OBJECT DeviceObject | Покажчик на об'єкт пристрою, якому був адресований даний об'єкт IRP |
PFILE_OBJECT FileObject | Файловий об'єкт для даного запиту, якщо він заданий |
union Parameters (трактування визначається значенням MajorFunction) | |
struct Read | Параметри для IRP типу IRP _ MJ _ READ: |
ULONG Length ULONG Key LARGE_INTEGER ByteOffset | |
struct Write | Параметри для IRP типу IRP _ MJ _ WRITE: ULONG Length ULONG Key LARGE_INTEGER ByteOffset |
struct DeviceControl | Параметри для IRP типу IRP _ MJ _ DEVICE _ CONTROL: ULONG OutputBufferLength ULONG InputBufferLength ULONG IoControlCode PVOID Type3InputBuffer |
Наведемо графічне представлення структури IRP пакету:
Рис. 1.5.1 Структура IRP пакету
Спілкування з USB накопичувачами в ОС Windows NT 5 на рівні драйверів, як вже було сказано в розділі 1.3.1, відбувається за допомогою передачі URB пакетів. Покажчики на URB пакети містять осередки стека IRP пакету, доступ до цих вказівниками здійснюється наступним чином:
...
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation (Irp);
PURB Urb = IrpSp-> Parameters. Others. Argument 1;
...
Наведемо часткове оголошення структури з довідкової документації Microsoft. Відзначимо лише поля, використання яких необхідно в рамках даної курсової роботи:
typedef struct _URB {
union {
struct _URB_HEADER UrbHeader;
struct _URB_SELECT_INTERFACE UrbSelectInterface;
struct _URB_SELECT_CONFIGURATION UrbSelectConfiguration;
struct _URB_BULK_OR_INTERRUPT_TRANSFER UrbBulkOrInterruptTransfer;
}
} URB, * PURB;
Поле UrbHeader зберігає інформацію про код URB пакету, за яким можна визначити, яка операція запитується.
Поля UrbSelectInterface і UrbSelectConfiguration служать для запиту по вибору інтерфейсу та конфігурації пристрою, які будуть використовуватися при роботі з пристроєм. Пакети цієї структури відправляються хостом до пристрою на початку його роботи, при конфігуруванні.
Поле UrbBulkOrInterruptTransfer несе найважливішу в рамках даної курсової роботи інформацію - покажчики на блоки введення / виводу USB пристрою. Наведемо опис структури _URB_BULK_OR_INTERRUPT_TRANSFER:
struct _URB_BULK_OR_INTERRUPT_TRANSFER {
struct _URB_HEADER Hdr;
USBD_PIPE_HANDLE PipeHandle;
ULONG TransferFlags;
ULONG TransferBufferLength;
PVOID TransferBuffer;
PMDL TransferBufferMDL;};
Поля цієї структури описані в наступній таблиці:
Таблиця 1.5.3 Поля структури URB_BULK_OR_INTERRUPT_TRANSFER
Поле | Опис |
struct _URB_HEADER Hdr | Стандартний заголовок URB пакета, що містить код запиту |
USBD_PIPE_HANDLE PipeHandle | Дескриптор каналу, на який передаються дані |
ULONG TransferFlags | Прапори, що визначають напрямок передачі даних і спосіб обробки помилок |
ULONG TransferBufferLength | Довжина передаваного блоку даних в байтах |
PVOID TransferBuffer | Покажчик на переданий буфер. Буфер знаходиться в нестранічной пам'яті |
PMDL TransferBufferMDL | Покажчик на MDL список, що несе передану інформацію. Буфер знаходиться в сторінкової пам'яті |
Слід зазначити, що один з покажчиків TransferBuffer або TransferBufferMDL дорівнює NULL, тобто в межах одного пакету передається тільки одна порція даних.
Завдання протоколювання обміну інформацією зводиться до перехоплення і збереження буферів TransferBuffer і TransferBufferMDL.
1.6 Рівні запиту переривань
У кожний момент часу центральний процесор знаходиться на одному з рівнів IRQL (Interrupt Request Level - рівень запитів переривань). Рівні IRQL розташовуються в порядку убування від HIGHEST _ LEVEL до PASSIVE _ LEVEL. Кожному з переривань (переривання від зовнішніх пристроїв, системний годинник, і т.д.) відповідає свій рівень IRQL. Спеціальним діям операційної системи також призначені IRQL. Вони відзначені в нижній частині наведеної таблиці:
Таблиця 1.6.1. Рівні запитів переривань.
Рівень | Призначення |
HIGHEST_LEVEL | Найвищий рівень. Всі переривання заблоковані |
POWER_LEVEL | Переривання по відмові харчування |
IPI_LEVEL | Межпроцессорной взаємодія |
CLOCK2_LEVEL | Переривання по системному таймеру 2 |
З LOCK1_LEVEL | Переривання по системному таймером 1 |
PROFILE_LEVEL | Переривання по таймеру виміру продуктивності |
рівні DRQL | Звичайні переривання пристроїв |
DISPATCH_LEVEL | Диспетчеризація потоків і виконання відкладених процедур |
APC_LEVEL | Виконання асинхронного виклику процедури |
PASSIVE_LEVEL | Звичайне виконання коду потоку |
Загальне правило обробки рівнів запитів переривань свідчить, що переривання з IRQL, меншим, ніж у виконуваного в даний момент коду, маскуються. Під час виконання коду потоку (користувальницького або системного) встановлюється найменший IRQL = 0 (PASSIVE _ LEVEL). Робота драйвера найчастіше виконується на рівні IRQL = 2 (DISPATCH _ LEVEL). Рівні, що лежать над ним, називаються DIRQL (Device IRQL) і виставляються для обробників переривань від зовнішніх пристроїв (ISR - interrupt service routine). Навіть під час виконання ISR драйвера може відбутися переривання з великим IRQL, наприклад, що належить іншому драйверу.
Чим вище поточний рівень IRQL виконуваного коду, тим менше функцій йому доступно. Так, наприклад, диспетчер потоків працює на рівні
DISPATCH _ LEVEL, і, отже, не буде викликатися, поки на процесорі з рівнем більшим чи рівним DISPATCH _ LEVEL виповнюється інший код. Таким чином, на рівнях DISPATCH _ LEVEL і вище відключається перемикання потоків. Функції очікування диспетчерських об'єктів (події, м'ютекси, семафори) з відмінною від нуля часом, звернення до файлів, підкачка відсутніх у фізичній пам'яті сторінок - все це також стає недоступним. Для коректного збереження запитів у файлі фільтр у таких випадках повинен застосовувати спеціальну методику.
1.7 Повідомлення про завершення запиту нижчестоящим драйвером
При відстеження обміну даними драйвер-фільтр може отримувати повідомлення про те, що деякий переданий запит був завершений нижчестоящим драйвером. Механізм повідомлення полягає в тому, що викликом спеціальної функції IoSetCompletionRoutine фільтр звертається до стека в пакеті IRP. У позиції стека, наступної за поточною позицією, він встановлює в спеціальному полі адресу функції завершення (completion routine). Потім при передачі пакета по ланцюжку позиція стека збільшується.
Коли нижчий драйвер відправляє пакет запиту на завершення (викликом IoCompleteRequest), підсистема вводу / Виводу починає проглядати стек всередині цього пакета від кінця до початку. Якщо в якійсь позиції стека визначена функція завершення, управління передається їй. Відпрацювавши, функція повертає результат, який сигналізує про успіх, помилку або необхідності подальшої обробки запиту.
При перших двох варіантах підсистема вводу / виводу переходить до наступної позиції стека і продовжує перегляд, поки не буде досягнуто його початок. Після цього запит завершується нормальним чином.
При третьому ж варіанті перегляд стека негайно припиняється і запит не буде завершено. Ця можливість реалізована для того, щоб драйвер-фільтр міг виконати будь-які дії над пакетом запиту після того, як той буде оброблений у нижчестоящих драйвері. Після такої «додаткової обробки» пакет знову повинен бути відправлений на завершення.
1.8 Робота з файлами в режимі ядра
Оскільки протоколіруемая інформація повинна зберігатися у файлі на диску, слід розглянути основні функції рівня ядра, використовувані при роботі з файлами.
Для відкриття файлу з драйвера режиму ядра використовується універсальна функція ZwCreateFile. Універсальність цієї функції полягає в тому, що з її допомогою здійснюється і відкриття існуючих файлів, і створення нових.
Специфіка системної функції ZwCreateFile полягає в тому, що вона має протокольних параметрів навіть більше, ніж користувальницький виклик CreateFile. Істотна частина вхідної інформації про відкривається об'єкті надходить всередині структури OBJECT _ ATTRIBURTES, яку слід заздалегідь створити і заповнити відповідними конкретними даними. Для ведення облікової інформації відкритого об'єкта використовується структура даних IO _ STATUS _ BLOCK, яку слід надати при виклику (ініціалізувати її не буде).
Наведемо основні параметри функції ZwCreateFile в наступній таблиці:
Таблиця 1.8.1. Параметри функції ZwCreateFile
Тип параметра | Опис параметра |
OUT PHANDLE pHandle | Покажчик на змінну, куди слід помістити дескриптор відкритого об'єкта |
IN ACCESS_MASK DesiredAccess | Характеристика доступу до об'єкта. Для фалів найчастіше використовуються значення GENERIC_READ або GENERIC_WRITE |
IN POBJECT_ATTRIBUTES pObjAttributes | Покажчик на заповнену викликає кодом структуру даних, яка описує ім'я, місце розташування і деякі інші характеристики відкривається об'єкта |
OUT PIO_STATUS_BLOCK pIOStatus | Покажчик на буфер, в якому буде розміщена інформація про відкритий об'єкті у форматі структури IO _ STATUS _ BLOCK |